//
// Created by 郝羽 on 2017/12/18.
//

#ifndef RTC_XSWITCH2_AIMD_RATE_CONTROL_TEST_HPP
#define RTC_XSWITCH2_AIMD_RATE_CONTROL_TEST_HPP

#include "test/catch.hpp"

#include "bwe/aimd_rate_control.hpp"

namespace bwe {
namespace test {

constexpr int64_t kClockInitialTime = 123456;

constexpr int kMinBwePeriodMsSmoothingExp = 500;
constexpr int kMinBwePeriodMsNoSmoothingExp = 2000;
constexpr int kDefaultPeriodMsNoSmoothingExp = 3000;
constexpr int kMaxBwePeriodMs = 50000;
constexpr char kSmoothingExpFieldTrial[] =
        "WebRTC-Audio-BandwidthSmoothing/Enabled/";

// After an overuse, we back off to 85% to the received bitrate.
constexpr double kFractionAfterOveruse = 0.85;

class RateControlTest {
public:
    AimdRateControl aimd_rate_control;
    int64_t         now_ms = kClockInitialTime;
};

void UpdateRateControl(RateControlTest& test,
                       bwe::Usage usage,
                       int bitrate,
                       int64_t now_ms) {
    bwe::RateControlInput input;
    input.usage = usage;
    input.bitrate = bitrate;
    input.has_bitrate = true;
    input.noise_var = now_ms;
    test.aimd_rate_control.Update(input, now_ms);
}

TEST_CASE("min near max increase rate on low bandwidth", "[rate-control]") {
    RateControlTest test;
    test.aimd_rate_control.SetEstimate(30000, test.now_ms);
    REQUIRE(test.aimd_rate_control.GetNearMaxIncreaseRateBps(200) == 4000);
}

TEST_CASE("near max increase rate is 5 kbps on 90kbps and 200ms rtt", "[rate-control]") {
    RateControlTest test;
    test.aimd_rate_control.SetEstimate(90000, test.now_ms);
    REQUIRE(test.aimd_rate_control.GetNearMaxIncreaseRateBps(200) == 5000);
}

TEST_CASE("near max increase rate is 5kbps on 60kbps and 100ms rtt", "[rate-control]") {
    RateControlTest test;
    test.aimd_rate_control.SetEstimate(60000, test.now_ms);
    REQUIRE(test.aimd_rate_control.GetNearMaxIncreaseRateBps(100) == 5000);
}

TEST_CASE("get increase rate and bandwidth period", "[rate-control]") {
    // Smoothing experiment disabled
    RateControlTest test;
    test.aimd_rate_control.SetEstimate(300000, test.now_ms);
    UpdateRateControl(test, bwe::Usage::OverUsing, 300000, test.now_ms);
    int near_max_bps = test.aimd_rate_control.GetNearMaxIncreaseRateBps(200);
    int near_max_bps_diff = near_max_bps - 14000;
    REQUIRE(std::abs(near_max_bps_diff) <= 1000);
    REQUIRE(test.aimd_rate_control.GetExpectedBandwidthPeriodMs() == kDefaultPeriodMsNoSmoothingExp);
}

// GetIncreaseRateAndBandwidthPeriodSmoothingExp was skipped because we don't have smoothing experiment functions


TEST_CASE("bwe limited by acked bitrate", "[rate-control]") {
    RateControlTest test;

    constexpr int kAckedBitrate = 10000;
    test.aimd_rate_control.SetEstimate(10000, test.now_ms);
    while (test.now_ms - kClockInitialTime < 20000) {
        UpdateRateControl(test, bwe::Usage::Normal, 10000, test.now_ms);
        test.now_ms += 100;
    }
    REQUIRE(test.aimd_rate_control.HasBps());
    REQUIRE(test.aimd_rate_control.LatestEstimate() == static_cast<uint32_t>(1.5 * 10000 + 10000));
}

TEST_CASE("bwe not limited by decreasing acked bitrate", "[rate-control]") {
    RateControlTest test;
    constexpr int kAckedBitrate = 100000;
    test.aimd_rate_control.SetEstimate(kAckedBitrate, test.now_ms);
    while (test.now_ms - kClockInitialTime < 20000) {
        UpdateRateControl(test, bwe::Usage::Normal, kAckedBitrate, test.now_ms);
        test.now_ms += 100;
    }
    REQUIRE(test.aimd_rate_control.HasBps());
    // If the acked bitrate decreases the BWE shouldn't be reduced to 1.5x
    // what's being acked, but also shouldn't get to increase more.
    uint32_t prev_estimate = test.aimd_rate_control.LatestEstimate();
    UpdateRateControl(test, bwe::Usage::Normal, kAckedBitrate/2, test.now_ms);
    uint32_t new_estimate = test.aimd_rate_control.LatestEstimate();

    int estimate_diff = new_estimate - static_cast<uint32_t>(1.5 * kAckedBitrate + 10000);
    REQUIRE(std::abs(estimate_diff) <= 2000);
    REQUIRE(new_estimate == prev_estimate);
}

TEST_CASE("default period until first overuse", "[rate-control]") {
    // Smoothing experiment disabled
    RateControlTest test;
    test.aimd_rate_control.SetStartBitrate(300000);
    REQUIRE(test.aimd_rate_control.GetExpectedBandwidthPeriodMs() == kDefaultPeriodMsNoSmoothingExp);
    test.now_ms += 100;
    UpdateRateControl(test, bwe::Usage::OverUsing, 280000, test.now_ms);
    REQUIRE(test.aimd_rate_control.GetExpectedBandwidthPeriodMs() != kDefaultPeriodMsNoSmoothingExp);
}

// MinPeriodUntilFirstOveruseSmoothingExp was skipped because we don't have smoothing experiment functions

TEST_CASE("expected period after 20kbps drop and 5kbps increase", "[rate-control]") {
    RateControlTest test;
    constexpr int kInitialBitrate = 110000;
    test.aimd_rate_control.SetEstimate(kInitialBitrate, test.now_ms);
    test.now_ms += 100;
    // Make the bitrate drop by 20 kbps to get to 90 kbps.
    // The rate increase at 90 kbps should be 5 kbps, so the period should be 4 s.
    constexpr int kAckedBitrate = (kInitialBitrate - 20000) / kFractionAfterOveruse;
    UpdateRateControl(test, bwe::Usage::OverUsing, kAckedBitrate, test.now_ms);
    REQUIRE(test.aimd_rate_control.GetNearMaxIncreaseRateBps(200) == 5000);
    REQUIRE(test.aimd_rate_control.GetExpectedBandwidthPeriodMs() == 4000);
}

// MinPeriodAfterLargeBitrateDecreaseSmoothingExp was skipped because we don't have smoothing experiment functions

TEST_CASE("bandwidth period is not below min", "[rate-control]") {
    RateControlTest test;

    constexpr int kInitialBitrate = 10000;
    test.aimd_rate_control.SetEstimate(kInitialBitrate, test.now_ms);
    test.now_ms += 100;
    // Make a small (1.5 kbps) bitrate drop to 8.5 kbps.
    UpdateRateControl(test, bwe::Usage::OverUsing, kInitialBitrate - 1, test.now_ms);
    REQUIRE(test.aimd_rate_control.GetExpectedBandwidthPeriodMs() == kMinBwePeriodMsNoSmoothingExp);
}

// BandwidthPeriodIsNotAboveMaxSmoothingExp was skipped because we don't have smoothing experiment functions

TEST_CASE("bandwidth period is not above max no smoothing exp", "[rate-control]") {
    RateControlTest test;
    constexpr int kInitialBitrate = 10010000;
    test.aimd_rate_control.SetEstimate(kInitialBitrate, test.now_ms);
    test.now_ms += 100;
    // Make a large (10 Mbps) bitrate drop to 10 kbps.
    constexpr int kAckedBitrate = 10000 / kFractionAfterOveruse;
    UpdateRateControl(test, bwe::Usage::OverUsing, kAckedBitrate, test.now_ms);
    REQUIRE(test.aimd_rate_control.GetExpectedBandwidthPeriodMs() == kMaxBwePeriodMs);
}

}
}

#endif //RTC_XSWITCH2_AIMD_RATE_CONTROL_TEST_HPP
